<?php 

	class dba_mysql{
		// $rsrc : holds the mysqli resource
		private $rsrc = false;
		
		// these variables hold the info neccessary to access the db
		private $user;
	 	private $pass;
		private $database;
		
		private $insert_queue;
		
		private $query = false;
		
		function __construct($user, $pass, $database, $host='localhost'){
			$this->user = $user;
			$this->pass = $pass;
			$this->database = $database;
			$this->host = $host;			
		}
		
		// report knows what to do when this abstraction layer wants to tell you something
		private function report($string='', $q=false, $die=false){
			if($q && is_callable(array($q, 'close'))) $q->close();
			$string .= mysqli_error($this->rsrc);
			$this->last_error = $string;
			if($die) httpE(500, $string);
			error_log($string);
			return false;
		}
		
		// open() : actually opens resource.  Is called before every query.
		function open(){
			if($this->rsrc) return $this->qci();
			$this->rsrc = new mysqli($this->host, $this->user, $this->pass, $this->database);
			if($n = mysqli_connect_errno($this->rsrc)){
				$this->close();
				$die = !getenv('SMUOI_SUPPRESS_MYSQL_FATALITIES');
				return $this->report("Connection to ".$this->database." failed, error $n: " . mysqli_connect_error($this->rsrc), false, $die);
			}
			$this->pass = sha1($this->pass . INSTANCE);
			smuoi_register_shutdown_function(array($this, 'close'));
			return $this->qci();
		}
		
		function check_pass($p){
			return ($this->pass == (($this->rsrc) ? sha1($p.INSTANCE) : $p)) ? $this : false;
		}

		// close() : closes this->rsrc
		function close(){
			if($this->rsrc && is_callable(array($this->rsrc, 'close'))) $this->rsrc->close();
			$this->rsrc = false;
		}
		
		function query($q){
			$this->open();
			return $this->query = mysqli_query($this->rsrc, $q);	
		}
		
		// qci() : increments query count somewhere for performance info
		function qci(){
			if($this->query && is_callable(array($this->query, 'close'))){
				//@$this->query->close();
				//$this->query = false;
			}
			sn()->MQC++;
			return true;
		}
		
		function execute($qs, $return=true){
			$this->open();
			if(($query = $this->query($qs)) && ($return) && (mysqli_num_rows($query) > 0)){	
				while($row = mysqli_fetch_array($query, MYSQLI_ASSOC)) $results[] = $row;
				$query->close();	
				return $results;
			}
			return ($return) ? $this->report($qs, $query) : $query;
		}
		
		// insert() 
		function insert($table, $data, $queue=false){
			$data = (array) $data;
			if($queue) return $this->join_insert_queue($table, $data);
			$cols = "`".implode("`, `", array_keys($data))."`";
			$vals = "'".implode("', '", array_map('addslashes', $data))."'";
			$qs = "INSERT INTO `$table` ($cols) VALUES ($vals);";
			$this->open();
			if($this->query($qs) === true) return true;
			return $this->report($qs);
		}
		
		private function join_insert_queue($table, $data){
			// Inserts must of course be run first... updates to inserted data in same run *can* happen occasionally!
			if(!$this->insert_queue) smuoi_jump_shutdown_queue(array($this, 'run_insert_queue'));
			$cols = "`".implode("`, `", array_keys($data))."`";
			$vals = "'".implode("', '", array_map('addslashes', $data))."'";
			$this->insert_queue[$table]["($cols)"][$data['id']] = "($vals)";
			return true;
		}
		
		function run_insert_queue(){
			if(!is_array($this->insert_queue)) return;
			foreach($this->insert_queue as $table=>$col_queue){
				foreach($col_queue as $cols=>$vals){
					$qs = "INSERT INTO `$table` $cols VALUES ".implode(', ', $vals).';';
					if(!$this->execute($qs, false)) $this->report($qs);
				}
			}
			$this->insert_queue = NULL;
		}
		
		// update() table set $columns, $values where $keys = $c_values
		function update($table, $set, $where){
			$qs = "UPDATE `$table` SET ".$this->update_set($set)." ".$this->where_condition($where).";";
			$this->open();
			if($this->query($qs) === true) return true;
			return $this->report($qs);
		}
		
		// select() : retrieves numerically indexed rows of associative arrays of data
		function select($fields, $table, $where, $orderby='', $limit=''){
			if(!is_scalar($fields)) $fields = implode(', ', (array)$fields);
			$qs = "SELECT $fields FROM `$table` ".$this->where_condition($where);
			if($orderby) $qs .= " ORDER BY $orderby";
			if($limit) $qs .= " LIMIT $limit";
			$qs .= ';';
			$this->open();
			if(($query = $this->query($qs)) && (mysqli_num_rows($query) > 0)){	
				while($row = mysqli_fetch_array($query, MYSQLI_ASSOC)) $results[] = $row;
				$query->close();	
				return $results;
			}
			return $this->report($qs, $query);
		}
		
		// count_rows() : uses mysqli to count table rows
		function count_rows($table, $where=1){
			$where = $this->where_condition($where);
			$qs = "SELECT COUNT(*) from `$table` $where;";
			$this->open();
			if($query = $this->query($qs)){
				$row = mysqli_fetch_row($query);
				$query->close();	
				return intval($row[0]);
			}
			return $this->report($qs, $query);
		}
			
		// exists() : simply checks if row exists
		function exists($table, $where){
			$field = key($where);
			$where = $this->where_condition($where);
			$qs = "SELECT `$field` FROM `$table` $where LIMIT 1;";
			$this->open();
			if($query = $this->query($qs)){
				$i = mysqli_num_rows($query);
				$query->close();
				return (bool) $i;
			}
			return $this->report($qs, $query);
		}
				
		// delete() : removes row(s) from db
		function delete($table, $where){
			$this->open();
			return $this->query("DELETE FROM `$table` ".$this->where_condition($where).";");
		}
		
		// tableCols() : retrieves array of columns on $table
		function table_cols($table){
			$qs = "SHOW COLUMNS FROM `$table`;";
			$this->open();
			if($query = $this->query($qs)){
				while($row = mysqli_fetch_assoc($query)) $results[] = $row['Field'];
				$query->close();
				return $results;
			}
			return $this->report($qs, $query);
		}

		// tableDefaults() : retrieves default value of each field on table
		function table_defaults($table){
			$qs = "SHOW COLUMNS FROM `$table`;";
			$this->open();
			if($query = $this->query($qs)){
				$results = array();
				while($row = mysqli_fetch_assoc($query)) $results[$row['Field']] = $row['Default'];
				$query->close();
				return $results;
			}
			return $this->report($qs, $query);
		}

		function where_condition($where){
			if(is_scalar($where)) return "WHERE $where";
			$conds = array();
			foreach($where as $k=>$v){
				switch(substr($k, 0, 1)){
					case '!':
						$k = substr($k, (($s = substr($k, 1, 1) == '%') ? 2 : 1));
						$op = ($s == '%') ? 'NOT LIKE' : '!=';
						$v = addslashes($v);
						break;
					case '%':
						$k = substr($k, 1);
						$v = addslashes($v);
						$op = 'LIKE';
						break;
					case '?':
						$k = substr($k, 1);
						$op = 'REGEXP';
						break;
					default:
						$v = addslashes($v);
						$op = '=';
				}
				$conds[] = "`$k` $op '$v'";
			}
			return 'WHERE ' .implode(' AND ', $conds);
		}
		
		function update_set($set){
			$uset = array();
			foreach($set as $k=>$v) $uset[] = "`$k` = '".addslashes($v)."'";
			return implode(', ', $uset);	
		}	
	}
		

